#pragma once
#include "../../Ode.h"
#include "../DSpace.h"

namespace Skill
{
	namespace Ode
	{
		ref class DBody;		
		ref class DVector3;
		ref class DVector4;
		ref class DMatrix3;
		ref class DMatrix4;
		ref class DQuaternion;
		ref class DAabb;
		ref class DContact;
		ref class DContactGeom;		
		ref class DContactInfoArray;

		namespace Geometries
		{	
			///<summary>
			/// Defines type of geometry
			///</summary>
			public enum class DGeometryType : int
			{
				Sphere = dSphereClass,
				Box = dBoxClass,
				Capsule = dCapsuleClass,
				Cylinder = dCylinderClass,
				Plane = dPlaneClass,
				Ray = dRayClass,
				Convex = dConvexClass,
				Transform = dGeomTransformClass,
				TriMesh = dTriMeshClass,
				Heightfield = dHeightfieldClass,				
				None
			};

			public ref class DGeometry abstract : DSpace
			{
			internal:
				inline dGeomID _Geom() {return (dGeomID)_This;}
			protected:
				DGeometry(DSpace^ container): DSpace(nullptr,false)
				{
					_Container = container;					
				}				
				virtual void CollectManagedMemory() override;
			public:

				~DGeometry()
				{
					if(!_Disposed)
					{
						if(_Free)
							DRegisteredObjects::Remove(this);
						CollectManagedMemory();
					}
					this->!DGeometry();					
				}
				!DGeometry();

				///<summary>
				/// Retrieves type of geometry
				///</summary>
				property DGeometryType Type
				{
					DGeometryType get()
					{
						return (DGeometryType)dGeomGetClass(_Geom());
					}
				}								

				///<summary>				
				/// Gets or set the body associated with a placeable geom.
				///</summary>
				///<remarks>
				/// Setting a body on a geom automatically combines the position vector and
				/// rotation matrix of the body and geom, so that setting the position or
				/// orientation of one will set the value for both objects. Setting a body
				/// ID of zero gives the geom its own position and rotation, independent
				/// from any body. If the geom was previously connected to a body then its
				/// new independent position/rotation is set to the current position/rotation
				/// of the body.
				///
				/// Calling these functions on a non-placeable geom results in a runtime
				/// error in the debug build of ODE.				
				///</remarks>
				REF_PROP_DECLARATION_GETSET(DBody,Body);

				///<summary>
				/// Gets or set the position vector of a placeable geom.				
				///</summary>
				///<remarks>
				/// If the geom is attached to a body, the body's position will also be changed.
				/// Calling this function on a non-placeable geom results in a runtime error in
				/// the debug build of ODE.
				///</remarks>				
				REF_PROP_DECLARATION_GETSET(DVector3,Position);


				///<summary>
				/// Gets or set the rotation matrix of a placeable geom.
				///</summary>
				///<remarks>
				/// If the geom is attached to a body, the body's rotation will also be changed.
				/// Calling this function on a non-placeable geom results in a runtime error in
				/// the debug build of ODE.
				///</remarks>
				REF_PROP_DECLARATION_GETSET(DMatrix3,Rotation);

				///<summary>
				/// Gets or set the rotation and positon matrix of a placeable geom.
				///</summary>
				REF_PROP_DECLARATION_GETSET(DMatrix3,WorldMatrix);


				///<summary>
				/// Gets or set the rotation of a placeable geom.
				///</summary>
				///<remarks>
				/// If the geom is attached to a body, the body's rotation will also be changed.
				///
				/// Calling this function on a non-placeable geom results in a runtime error in
				/// the debug build of ODE.
				///</remarks>
				REF_PROP_DECLARATION_GETSET(DQuaternion,Quaternion);


				///<summary>
				/// Retrieves the axis-aligned bounding box surrounds the given geom.
				///</summary>
				///<remarks>				
				/// This function may return a pre-computed cached bounding box, if it can
				/// determine that the geom has not moved since the last time the bounding
				/// box was computed.				
				///</remarks>
				virtual property DAabb^ Aabb
				{
					DAabb^ get() override;
				}


				///<summary>
				/// Determing if a geom is a space.
				///</summary>				
				virtual property bool IsSpace
				{
					bool get()override {return false;}
				}				


				///<summary>
				/// returns class id.
				///</summary>
				///<remarks>
				/// The ode classes are:
				/// DSphereClass
				/// DBoxClass
				/// DCylinderClass
				/// DPlaneClass
				/// DRayClass
				/// DConvexClass
				/// DGeomTransformClass
				/// DTriMeshClass
				/// DSimpleSpaceClass
				/// DHashSpaceClass
				/// DQuadTreeSpaceClass
				/// DFirstUserClass
				/// DLastUserClass				
				/// User-defined class will return their own number.
				///</remarks>				
				virtual property int ClassID
				{
					int get() override;
				}


				///<summary>
				/// Gets od set the "category" bitfield for the given geom.				
				/// The category bitfield is used by spaces to govern which geoms will
				/// interact with each other. The bitfield is guaranteed to be at least
				/// 32 bits wide. The default category values for newly created geoms
				/// have all bits set.
				///</summary>				
				VALUE_PROP_DECLARATION_GETSET(unsigned long,CategoryBits);


				///<summary>
				/// Gets or set the "collide" bitfield for the given geom.				
				/// The collide bitfield is used by spaces to govern which geoms will
				/// interact with each other. The bitfield is guaranteed to be at least
				/// 32 bits wide. The default category values for newly created geoms
				/// have all bits set.
				///</summary>
				VALUE_PROP_DECLARATION_GETSET(unsigned long,CollideBits);

				///<summary>
				/// Enable geometry				
				///</summary>
				void Enable();


				///<summary>				
				/// Disabled geoms are completely ignored by dSpaceCollide and dSpaceCollide2,
				/// although they can still be members of a space. New geoms are created in
				/// the enabled state.
				///</summary>
				void Disable();


				///<summary>
				/// Check to see if a geom is enabled.
				///</summary>				
				VALUE_PROP_DECLARATION_GET(bool,IsEnabled);
				
				///<summary>
				/// Gets or set the local offset position of a geom from its body.
				///</summary>
				///<remarks>
				/// Sets the geom's positional offset in local coordinates.
				/// After this call, the geom will be at a new position determined from the
				/// body's position and the offset.
				/// The geom must be attached to a body.
				/// If the geom did not have an offset, it is automatically created.
				///</remarks>
				REF_PROP_DECLARATION_GETSET(DVector3,OffsetPosition);


				///<summary>
				/// Gets or set the local offset rotation matrix of a geom from its body.
				///</summary>
				///<remarks>
				/// Sets the geom's rotational offset in local coordinates.
				/// After this call, the geom will be at a new position determined from the
				/// body's position and the offset.
				/// The geom must be attached to a body.
				/// If the geom did not have an offset, it is automatically created.
				///</remarks>
				REF_PROP_DECLARATION_GETSET(DMatrix3,OffsetRotation);


				///<summary>
				/// Gets or set the local offset rotation of a geom from its body.
				///</summary>
				///<remarks>
				/// Sets the geom's rotational offset in local coordinates.
				/// After this call, the geom will be at a new position determined from the
				/// body's position and the offset.
				/// The geom must be attached to a body.
				/// If the geom did not have an offset, it is automatically created.
				///</remarks>
				REF_PROP_DECLARATION_GETSET(DQuaternion,OffsetQuaternion);


				///<summary>
				/// Gets or set the offset position of a geom from its body.
				///</summary>
				///<remarks>
				/// Sets the geom's positional offset to move it to the new world
				/// coordinates.
				/// After this call, the geom will be at the world position passed in,
				/// and the offset will be the difference from the current body position.
				/// The geom must be attached to a body.
				/// If the geom did not have an offset, it is automatically created.
				///</remarks>								
				void SetOffsetWorldPosition(DVector3^ offset);


				///<summary>
				/// Set the offset rotation of a geom from its body.
				///</summary>
				///<remarks>				
				/// Sets the geom's rotational offset to orient it to the new world
				/// rotation matrix.
				/// After this call, the geom will be at the world orientation passed in,
				/// and the offset will be the difference from the current body orientation.
				/// The geom must be attached to a body.
				/// If the geom did not have an offset, it is automatically created.
				///</remarks>				
				void SetOffsetWorldRotation(DMatrix3^ offset);


				///<summary>
				/// Gets or set the offset rotation of a geom from its body.
				///</summary>
				///<remarks>
				/// Sets the geom's rotational offset to orient it to the new world
				/// rotation matrix.
				/// After this call, the geom will be at the world orientation passed in,
				/// and the offset will be the difference from the current body orientation.
				/// The geom must be attached to a body.
				/// If the geom did not have an offset, it is automatically created.
				///</remarks>								
				void SetOffsetWorldQuaternion(DQuaternion^ offset);


				///<summary>
				/// Clear any offset from the geom.
				///</summary>
				///<remarks>								
				/// If the geom has an offset, it is eliminated and the geom is
				/// repositioned at the body's position.  If the geom has no offset,
				/// this function does nothing.
				/// This is more efficient than calling dGeomSetOffsetPosition(zero)
				/// and dGeomSetOffsetRotation(identiy), because this function actually
				/// eliminates the offset, rather than leaving it as the identity transform.
				///</remarks>
				void ClearOffset();


				///<summary>
				/// Check to see whether the geom has an offset.
				///</summary>
				///<remarks>
				/// This function will return non-zero if the offset has been created.
				/// Note that there is a difference between a geom with no offset,
				/// and a geom with an offset that is the identity transform.
				/// In the latter case, although the observed behaviour is identical,
				/// there is a unnecessary computation involved because the geom will
				/// be applying the transform whenever it needs to recalculate its world
				/// position.
				///</remarks>
				VALUE_PROP_DECLARATION_GET(bool,IsOffset);

				static const int Contacts_Unimportant = CONTACTS_UNIMPORTANT;
				
				///<summary>
				///brief Given two geoms o1 and o2 that potentially intersect,
				/// generate contact information for them.
				///
				/// Internally, this just calls the correct class-specific collision
				/// functions for o1 and o2.
				///</summary>
				///<param name = "o1"> The first geom to test. </param>
				///<param name = "o1"> The second geom to test. </param>
				///<param name = "flags">
				/// The flags specify how contacts should be generated if
				/// the geoms touch. The lower 16 bits of flags is an integer that
				/// specifies the maximum number of contact points to generate. You must
				/// ask for at least one contact. 
				/// Additionally, following bits may be set:
				/// CONTACTS_UNIMPORTANT -- just generate any contacts (skip contact refining).
				/// All other bits in flags must be set to zero. In the future the other bits 
				/// may be used to select from different contact generation strategies.
				///</param>
				///<param name = "contacts">
				/// contact Points to an array of dContactGeom structures.
				///</param>			
				///<returns>
				/// returns If the geoms intersect, this function returns the number of contact
				/// points generated (and updates the contact array), otherwise it returns 0
				/// (and the contact array is not touched).
				///</returns>
				///<remarks>
				/// The array must be able to hold at least the maximum number of contacts.
				/// These dContactGeom structures may be embedded within larger structures in the
				/// array -- the skip parameter is the byte offset from one dContactGeom to
				/// the next in the array. If skip is sizeof(dContactGeom) then contact
				/// points to a normal (C-style) array. It is an error for skip to be smaller
				/// than sizeof(dContactGeom).			
				///
				/// If a space is passed as o1 or o2 then this function will collide
				/// all objects contained in o1 with all objects contained in o2, and return
				/// the resulting contact points. This method for colliding spaces with geoms
				/// (or spaces with spaces) provides no user control over the individual
				/// collisions. To get that control, use dSpaceCollide or dSpaceCollide2 instead.
				///
				/// If o1 and o2 are the same geom then this function will do nothing
				/// and return 0. Technically speaking an object intersects with itself, but it
				/// is not useful to find contact points in this case.
				///
				/// remarks This function does not care if o1 and o2 are in the same space or not
				/// (or indeed if they are in any space at all).			
				///</remarks>				
				static int Collide(DSpace^ o1, DSpace^ o2, int flags, DContactInfoArray^ contacts);

			};			
		}
	}
}